#include <stdio.h>
#include <string.h>

#include "usart.h"
#include "loopbuf.h"
#include "platform_config.h"


#define USART_TX_BUFLEN		256
#define USART_RX_BUFLEN		256


static void USART_Basic_Config(USART_TypeDef *usart, uint32_t baudrate);
static void USART_IO_config(USART_TypeDef *usart, int enable);
static void USART_PowerCtrl(USART_TypeDef *usart, FunctionalState state);
static void waitForDMA_Compleate(USART_TypeDef *usart);
static void DMA_Tx_Start(USART_TypeDef *usart, void *buffer, int len);
static void USART_Interrupt_Config(USART_TypeDef *UARTx);
static int USART_To_Channel(USART_TypeDef *UARTx);
static void DMA_Rx_Start(USART_TypeDef *usart, void *buffer, int len);


/* MARK for DMA interrupt */
static uint8_t DMA_TXING[3] = {0, 0, 0};

/* Loop-buffer for USART_Read() */
static LOOPBUF *USART_RX_BUF[3] = {NULL, NULL, NULL};

/* Caches for DMA transfer */
//static char DMA_RXD_CACHE[3][USART_RX_BUFLEN];
static char DMA_TXD_CACHE[3][USART_TX_BUFLEN];
static char DMA_RXD_CACHE[3];

/* Open-State for USART */
static int USART_IS_OPEN[3] = {0};


/*
** @ Open USART with Baudrate and N,8,1
** @ usart: USART1, USART2, USART3
**/
int USART_Open(USART_TypeDef *usart, uint32_t baudrate, int rxBufLen) {
	int index;
		
	/* get USART index */
	index = USART_To_Channel(usart);
	if(index == -1)
		return -1;
	
	if (USART_IS_OPEN[index]) {
		USART_Close(usart);
	}
	
	/* malloc the Reveive buffer */
	if(USART_RX_BUF[index]) {
		lpbuf_destroy(USART_RX_BUF[index]);
	}
	
	USART_RX_BUF[index] = lpbuf_create(rxBufLen);
	if (USART_RX_BUF[index] == NULL)
		return -1;
	
	/* Enable GPIO clock and USART clock*/
	USART_PowerCtrl(usart, ENABLE);
	
	/* config the TX and RX io */
	USART_IO_config(usart, TRUE);

	/* basic config */
	USART_Basic_Config(usart, baudrate);
	
	/* Enable USART DMA TX and RX request */
	USART_DMACmd(usart, /*USART_DMAReq_Rx |*/ USART_DMAReq_Tx, ENABLE);
	
	/* Enable the USART DMA RX */
	// DMA_Rx_Start(usart, &DMA_RXD_CACHE[index], 1);
	
	/* Remap the GPIO pins */
	// GPIO_PinRemapConfig(GPIO_Remap_USART1, ENABLE);
	
	/* config USART interrupt */
	USART_Interrupt_Config(usart);

	/* Enable USART */
	USART_Cmd(usart, ENABLE);
	
	USART_IS_OPEN[index] = TRUE;
	return 0;
}

/*
** @ Close USART
**/
int USART_Close(USART_TypeDef *USARTx) {
	int index;
	
	/* get USART index */
	index = USART_To_Channel(USARTx);
	if(index == -1)
		return -1;
	
	/* free the Reveive buffer */
	if(USART_RX_BUF[index]) {
		lpbuf_destroy(USART_RX_BUF[index]);
	}
	
	/* reset the DMA mark */
	DMA_TXING[index] = 0;
	
	/* this will turn-off the clock */
	USART_DeInit(USARTx);
	
	/* deinit the RXD and TXD as input */
	USART_IO_config(USARTx, FALSE);
	
	/* Release the channel */
	USART_IS_OPEN[index] = FALSE;
	return 0;
}

/*
** @ USART DMA write
** @ Max transfer size = USART_TX_BUFLEN
**/
int USART_Write(USART_TypeDef *USARTx, const char *buffer, int len) {
	int index;
	
	/* get USART index */
	index = USART_To_Channel(USARTx);
	if(index == -1)
		return -1;
	
	/* wait for last DMA transfer */
	waitForDMA_Compleate(USARTx);
	
	/* copy to DMA cache */
	memcpy(DMA_TXD_CACHE[index], buffer, len);
	
	/* TX DMA configure */
	DMA_Tx_Start(USARTx, (void *)DMA_TXD_CACHE[index], len);
	
	/* wait for transfer end */
	// while (DMA_GetFlagStatus(USART3_Tx_DMA_FLAG) == RESET);
	return len;
}

/*
** @ USART Non-Bloking read
**/
int USART_Read(USART_TypeDef *USARTx, char *buffer, int len) {
	int count;
	int index;
	DMA_Channel_TypeDef *mark;
	
	index = USART_To_Channel(USARTx);
	if(index == -1)
		return -1;

//	switch((uint32_t)USARTx) {
//	case (uint32_t)USART1:
//		mark = USART1_Rx_DMA_Channel;
//	break;
//	case (uint32_t)USART2:
//		mark = USART2_Rx_DMA_Channel;
//	break;
//	case (uint32_t)USART3:
//		mark = USART3_Rx_DMA_Channel;
//	break; 
//	}
	/* disable the RX interrupt */
	USART_ITConfig(USARTx, USART_IT_RXNE, DISABLE);
//	DMA_ITConfig(mark, DMA_IT_TC, DISABLE);	
	
	/* read data from loopbuffer */
	count = lpbuf_read(USART_RX_BUF[index], buffer, len);
	
	/* enable the RX interrupt */
	USART_ITConfig(USARTx, USART_IT_RXNE, ENABLE);
//	DMA_ITConfig(mark, DMA_IT_TC, ENABLE);
	
	return count;
}

/*
** @ USART write one byte
**/
int USART_Putc(USART_TypeDef *USARTx, int c) {
	USART_SendData(USARTx, c);
    
	/* Loop until USARTy DR register is empty */ 
	while(USART_GetFlagStatus(USARTx, USART_FLAG_TXE) == RESET);
	return c;
}

/* USART interrupt configuration */
static void USART_Interrupt_Config(USART_TypeDef *UARTx) {
	/* enable the Reveive interrupt */
	USART_ITConfig(UARTx, USART_IT_RXNE, ENABLE);
	
	/* enable the idle interrupt */
	// USART_ITConfig(UARTx, USART_IT_IDLE, ENABLE);
}

/* return index for USART from 0 to 2 */
static int USART_To_Channel(USART_TypeDef *UARTx) {
	switch((uint32_t)UARTx) {
	case (uint32_t)USART1:
		return 0;
	case (uint32_t)USART2:
		return 1;
	case (uint32_t)USART3:
		return 2;
	default:
		return -1;
	}
}

/* USART basic configuration */
static void USART_Basic_Config(USART_TypeDef *usart, uint32_t baudrate) {
	USART_InitTypeDef USART_InitStructure;
	
	/* USART configuration */
	USART_InitStructure.USART_BaudRate = baudrate;
	USART_InitStructure.USART_WordLength = USART_WordLength_8b;
	USART_InitStructure.USART_StopBits = USART_StopBits_1;
	USART_InitStructure.USART_Parity = USART_Parity_No;
	USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
	USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
	
	USART_Init(usart, &USART_InitStructure);
}

/* config the USART TX and RX pin */
static void USART_IO_config(USART_TypeDef *usart, int enable) {
	GPIO_InitTypeDef GPIO_InitStructure;
	GPIO_TypeDef *GPIOx;
	uint16_t rxPin;
	uint16_t txPin;
	
	switch((uint32_t)usart) {
	case (uint32_t)USART1:
		GPIOx = USART1_GPIO;
		rxPin = USART1_RxPin;
		txPin = USART1_TxPin;
	break;
	case (uint32_t)USART2:
		GPIOx = USART2_GPIO;
		rxPin = USART2_RxPin;
		txPin = USART2_TxPin;
	break;
	case (uint32_t)USART3:
		GPIOx = USART3_GPIO;
		rxPin = USART3_RxPin;
		txPin = USART3_TxPin;
	break;
	}
	
	/* Configure USART Tx as alternate function push-pull */
	if (enable) {
		GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
		GPIO_InitStructure.GPIO_Pin = txPin;
		GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
		GPIO_Init(GPIOx, &GPIO_InitStructure);	
	} else {
		/* config RXD and TXD input */
		GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
		GPIO_InitStructure.GPIO_Pin = txPin;
		GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
		GPIO_Init(GPIOx, &GPIO_InitStructure);	
	}

	/* Configure USART Rx as input floating */
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
	GPIO_InitStructure.GPIO_Pin = rxPin;
	
	GPIO_Init(GPIOx, &GPIO_InitStructure);
}

/* enable or disable the USART clock */
static void USART_PowerCtrl(USART_TypeDef *usart, FunctionalState state) {
	switch((uint32_t)usart) {
	case (uint32_t)USART1:
		RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, state);
	break;
	case (uint32_t)USART2:
		RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2, state);
	break;
	case (uint32_t)USART3:
		RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART3, state);
	break;
	}
}

#if (0)
/* USART RXD DMA config */
static void DMA_Rx_Start(USART_TypeDef *USARTx, void *buffer, int len) {
	DMA_InitTypeDef DMA_InitStructure;
	DMA_Channel_TypeDef *mark;
	uint32_t DR_Base;

	switch((uint32_t)USARTx) {
	case (uint32_t)USART1:
		mark = USART1_Rx_DMA_Channel;
		DR_Base = USART1_DR_Base;
	break;
	case (uint32_t)USART2:
		mark = USART2_Rx_DMA_Channel;
		DR_Base = USART2_DR_Base;
	break;
	case (uint32_t)USART3:
		mark = USART3_Rx_DMA_Channel;
		DR_Base = USART3_DR_Base;
	break; 
	}
	
	DMA_DeInit(mark);  
  //DMA_DeInit(USART3_Rx_DMA_Channel);  
  //DMA_InitStructure.DMA_PeripheralBaseAddr = USARTy_DR_Base;
  //DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)TxBuffer1;
  //DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST;
  //DMA_InitStructure.DMA_BufferSize = TxBufferSize1;
	DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
	DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
	DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;
	DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;
	DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;
	DMA_InitStructure.DMA_Priority = DMA_Priority_VeryHigh;
	DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;
  // DMA_Init(USARTy_Tx_DMA_Channel, &DMA_InitStructure);
	
	DMA_InitStructure.DMA_PeripheralBaseAddr = DR_Base;
	DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)buffer;
	DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;
	DMA_InitStructure.DMA_BufferSize = len;
	DMA_Init(mark, &DMA_InitStructure);
	
	/* enable the transfer compleate interrupt */
	DMA_ITConfig(mark, DMA_IT_TC, ENABLE);
	
	/* Enable USART3 RX DMA3 Channel */
	DMA_Cmd(mark, ENABLE);
}
#endif

/* USART TXD DMA config */
static void DMA_Tx_Start(USART_TypeDef *usart, void *buffer, int len) {
	DMA_InitTypeDef DMA_InitStructure;
	DMA_Channel_TypeDef *DMA_Channel;
	uint32_t DR_Base;
	
	switch((uint32_t)usart) {
	case (uint32_t)USART1:
		DMA_Channel = USART1_Tx_DMA_Channel;
		DR_Base = USART1_DR_Base;
		DMA_TXING[0] = 1;
	break;
	case (uint32_t)USART2:
		DMA_Channel = USART2_Tx_DMA_Channel;
		DR_Base = USART2_DR_Base;
		DMA_TXING[1] = 1;
	break;
	case (uint32_t)USART3:
		DMA_Channel = USART3_Tx_DMA_Channel;
		DR_Base = USART3_DR_Base;
		DMA_TXING[2] = 1;
	break;
	}
	
	/* USARTy_Tx_DMA_Channel (triggered by USARTy Tx event) Config */
	DMA_DeInit(DMA_Channel);

	DMA_InitStructure.DMA_PeripheralBaseAddr = DR_Base;
	DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)buffer;
	DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST;
	DMA_InitStructure.DMA_BufferSize = len;
	DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
	DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
	DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;
	DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;
	DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;
	DMA_InitStructure.DMA_Priority = DMA_Priority_VeryHigh;
	DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;
	DMA_Init(DMA_Channel, &DMA_InitStructure);
	
	/* enable DMA channel */
	DMA_ITConfig(DMA_Channel, DMA_IT_TC, ENABLE);
	DMA_Cmd(DMA_Channel, ENABLE);
}

/* wait for last DMA transfer */
static void waitForDMA_Compleate(USART_TypeDef *usart) {
	switch((uint32_t)usart) {
	case (uint32_t)USART1:
		while (DMA_TXING[0]);
	break;
	case (uint32_t)USART2:
		while (DMA_TXING[1]);
	break;
	case (uint32_t)USART3:
		while (DMA_TXING[2]);
	break;
	}
}

/* USART DMA transfer compleate interrupt handler */
void USART_DMA_Tx_Interrupt_Handler(int index) {
	DMA_TXING[index] = 0;
}

/* USART RXD interrupt handler */
void USART_Rxd_Interrupt_Handler(int index, char c) {
	if(USART_RX_BUF[index]) {
		lpbuf_write(USART_RX_BUF[index], &c, 1);
	}	
	// DMA_Rx_Start(USART3, DMA_RXD_CACHE[index], 1);
}

// TEST ONLY ****** TEST ONLY ****** TEST ONLY ****** TEST ONLY ****** TEST ONLY ******
#include "tick.h"

int USART_Main() {
	char buf[128];
	char buf2[128];
	const char clearScreen = 0x0C;
	int count = 0;
	int nbytes;
	
	USART_Open(USART1, 115200, 64);

#if (0)	
	while (1) {
		USART_Write(USART1, &clearScreen, 1);
		count ++;
		sprintf(buf, "%d", count);
		USART_Write(USART1, buf, strlen(buf));
		//nbytes = USART_Read(USART2, buf2, sizeof(buf2));
		//if(nbytes) {
		//	USART_Write(USART3, buf2, nbytes);
		//}
		delay_ms(1000);
	}
#else
	while(1) {
		nbytes = USART_Read(USART1, buf2, sizeof(buf2));
		if(nbytes) {
			USART_Write(USART1, buf2, nbytes);
		}
		delay_ms(10);
	}
#endif	
}
